/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package assignment2;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 *
 * @author Christopher
 */
public class Assignment2 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException 
    {
        XYSeries series = new XYSeries("Price");
        XYSeries madSeries = new XYSeries("MAD");
        XYSeries boughtSoldSeries = new XYSeries("Shares Bought or Sold");
        XYSeries balanceSeries = new XYSeries("Balance");
        
        double totalRoi = 0.0;
        double[] testResultRoi = new double[500];
        //for(int test = 0; test < 500; test++ )
        //{
            String filename = "Assignment2Results.txt";
            PrintWriter writer = new PrintWriter(filename, "UTF-8");
            writer.println("account balance;stocks held;price;mad;shares bought;shares sold;daily profit;daily loss;accumulated profit; accumulated loss;total account value");
            double accountBalance = 10000.0;
            double accountValue = 0.0;
            int stockCount = 0;
            double price = 0.0;
            for(int i=0; i < 150; i++)
            {
                Assignment2 a2 = new Assignment2();
                writer.print(accountBalance + ";" + stockCount +";");
                price = a2.getPrice(i);
                series.add(i, price);
                writer.print(price+";");
                double mad = a2.getMAD(i);
                madSeries.add(i, mad);
                writer.print(mad+";");
                //Fuzzification
                double[] priceMembership = a2.determinePriceMembership(price);
                double[] madMembership = a2.determineMADMembership(mad);
                //Rule Evaluation
                double[] ruleEval = a2.evaluateRules(priceMembership, madMembership);
                
                double[] sugenoConstants = new double[5];
                sugenoConstants[0] = 700;
                sugenoConstants[1] = 350;
                sugenoConstants[2] = 0;
                sugenoConstants[3] = -350;
                sugenoConstants[4] = -700;
                double wa = a2.getSugenoWeightedAverage(ruleEval, sugenoConstants);
                //Defuzzification
                if(wa < 0.0)
                {
                    wa = Math.ceil(wa - 0.5);
                }
                else
                {
                    wa = Math.floor(wa + 0.5);
                }
                int intWa = (int)wa;
                if(intWa > 0)
                {
                    double purchasePrice = intWa*price;
                    double buyRecommendation = 0.0;
                    if(purchasePrice > accountBalance) //recommended we buy more than we can, so buy as much as we can
                    {
                        buyRecommendation = Math.floor(accountBalance/price);
                    }
                    else
                    {
                        buyRecommendation = intWa;
                    }                    
                    String sharesBought = buyRecommendation + ";0;";
                    boughtSoldSeries.add(i, buyRecommendation);
                    writer.print(sharesBought);
                    accountBalance -= buyRecommendation*price;
                    stockCount+=buyRecommendation;
                }
                else if(intWa < 0)
                {
                    double sellRecommendation = 0.0;
                    if (Math.abs(intWa) > stockCount)
                    {
                        sellRecommendation = stockCount;
                    }
                    else
                    {
                        sellRecommendation = Math.abs(intWa);
                    }
                    String sharesSold = "0;" + sellRecommendation + ";";
                    boughtSoldSeries.add(i, -1.0*sellRecommendation);
                    writer.print(sharesSold);
                    accountBalance += price*sellRecommendation;
                    stockCount -= sellRecommendation;
                }
                else
                {
                    writer.print("0;0;");
                    boughtSoldSeries.add(i, 0.0);
                }
                double valueYesterday = accountValue;
                accountValue = accountBalance + (stockCount * price);
                double dailyChange = accountValue - valueYesterday;
                if(dailyChange > 0.0)
                {
                    String dailyChangeStr = dailyChange + ";0;";
                    writer.print(dailyChangeStr);
                }
                else
                {
                    String dailyChangeStr = "0;" + dailyChange + ";";
                    writer.print(dailyChangeStr);
                }
                double totalAssets = accountBalance + (stockCount * price);
                balanceSeries.add(i, totalAssets);
                double gain = totalAssets-10000.0;
                if(gain > 0.0)
                {
                    String TotalChange = gain + ";0;";
                    writer.print(TotalChange);
                }
                else
                {
                    String TotalChange = "0;" + gain + ";";
                    writer.print(TotalChange);
                }
                writer.println(totalAssets);
            }
            
            double totalAssets = accountBalance + (stockCount * price);
            
            double gain = totalAssets-10000.0;
            double roi = gain/10000.0;
          //  testResultRoi[test] = roi;
            totalRoi += roi;
            writer.close();
        //}
        double averageROI = totalRoi/500;
        //System.out.println("Result - Average ROI: " + averageROI);
        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series);
        JFreeChart chart = ChartFactory.createXYLineChart(
            "Price by day", // Title
            "Day", // x-axis Label
            "Stock Price in $", // y-axis Label
            dataset, // Dataset
            PlotOrientation.VERTICAL, // Plot Orientation
            true, // Show Legend
            true, // Use tooltips
            false // Configure chart to generate URLs?
            );
        BufferedImage image = chart.createBufferedImage(600,390);
        JLabel lblChart = new JLabel();
        lblChart.setIcon(new ImageIcon(image));
        
        XYSeriesCollection madDataset = new XYSeriesCollection();
        madDataset.addSeries(madSeries);
        JFreeChart madChart = ChartFactory.createXYLineChart(
            "MAD by day", // Title
            "Day", // x-axis Label
            "MAD", // y-axis Label
            madDataset, // Dataset
            PlotOrientation.VERTICAL, // Plot Orientation
            true, // Show Legend
            true, // Use tooltips
            false // Configure chart to generate URLs?
            );
        BufferedImage madImage = madChart.createBufferedImage(600,390);
        JLabel lblMadChart = new JLabel();
        lblMadChart.setIcon(new ImageIcon(madImage));
                
        XYSeriesCollection balanceDataset = new XYSeriesCollection();
        balanceDataset.addSeries(balanceSeries);
        JFreeChart balanceChart = ChartFactory.createXYLineChart(
            "Asset Balance", // Title
            "Day", // x-axis Label
            "Balance in $", // y-axis Label
            balanceDataset, // Dataset
            PlotOrientation.VERTICAL, // Plot Orientation
            true, // Show Legend
            true, // Use tooltips
            false // Configure chart to generate URLs?
            );
        BufferedImage balanceImage = balanceChart.createBufferedImage(600,390);
        JLabel lblBalanceChart = new JLabel();
        lblBalanceChart.setIcon(new ImageIcon(balanceImage));
        
        XYSeriesCollection boughtSoldDataset = new XYSeriesCollection();
        boughtSoldDataset.addSeries(boughtSoldSeries);
        JFreeChart boughtSoldChart = ChartFactory.createXYLineChart(
            "Stocks Bought or Sold", // Title
            "Day", // x-axis Label
            "Count", // y-axis Label
            boughtSoldDataset, // Dataset
            PlotOrientation.VERTICAL, // Plot Orientation
            true, // Show Legend
            true, // Use tooltips
            false // Configure chart to generate URLs?
            );
        BufferedImage boughtSoldImage = boughtSoldChart.createBufferedImage(600,390);
        JLabel lblBoughSoldChart = new JLabel();
        lblBoughSoldChart.setIcon(new ImageIcon(boughtSoldImage));
        
        GridLayout testLayout = new GridLayout(0,2);
        JFrame frame = new JFrame("Price Graph");
        		frame.setBounds(50,50,1200,800);
        frame.setLayout(testLayout);
        frame.add(lblChart);
        frame.add(lblMadChart);
        frame.add(lblBoughSoldChart);
        frame.add(lblBalanceChart);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setVisible(true);
    }
    
    public double getPrice(int day)
    {
        double price;
        double randomVal;
        Random random = new Random();
        randomVal = (random.nextDouble() * 2.0) - 1.0;
        price = 12 + (2.5*Math.sin((2*Math.PI*day)/19)) + (0.8*Math.cos((2*Math.PI*day)/5)) + (8*randomVal*(day%2));
        return price;
    }
    
    public double getMAD(int day)
    {
        double mad;
        mad = 0.5 * Math.cos(0.3*day)-Math.sin(0.3*day);
        return mad;
    }
    
    public double[] determineMADMembership(double mad)
    {
        double[] membership = new double[3];
        double normalMAD = mad/1.12;
        membership[0] = this.determineNMebership(normalMAD);
        membership[1] = this.determineZMebership(normalMAD);
        membership[2] = this.determinePMebership(normalMAD);
        
        return membership;
    }
    
    public double determineNMebership(double mad)
    {
        double membership = 0.0;
        if(mad <= -0.5)
        {
            membership = 1.0;
        }
        else if(mad > -0.5 && mad < 0.0)
        {
            membership = 1-(-1/0.5)*(mad);
        }
        return membership;
    }
    
    public double determineZMebership(double mad)
    {
        double membership = 0.0;
        if(mad >= -0.5 && mad < 0)
        {
            membership = (1.0/0.5)*mad + 1;
        }
        else if(mad >= 0.0 && mad < 0.5)
        {
            membership = (-1/0.5)*(mad)+1;
        }
        return membership;
    }
    
    public double determinePMebership(double mad)
    {
        double membership = 0.0;
        if(mad >= 0.0 && mad < 0.5)
        {
            membership = (1.0/0.5)*mad;
        }
        else if(mad >= 0.5)
        {
            membership = 1;
        }
        return membership;
    }
    
    public double[] determinePriceMembership(double price)
    {
        double normalPrice = (price - 0.7) / (23.29-0.7);
        double[] membership = new double[5];
        membership[0] = this.determineVLMembership(normalPrice);
        membership[1] = this.determineLOMembership(normalPrice);
        membership[2] = this.determineMDMembership(normalPrice);
        membership[3] = this.determineHIMembership(normalPrice);
        membership[4] = this.determineVHMembership(normalPrice);
        
        return membership;
    }
    private double determineVLMembership(double price)
    {
        double membership = 0.0;
        if(price <= 0.15)
        {
            membership = 1.0;
        }
        else if(price > 0.15 && price <= 0.325)
        {
            membership = 1-((-1/.175)*(price-0.15));
        }
        return membership;
    }
    private double determineLOMembership(double price)
    {
        double membership = 0.0;
        
        if(price >= 0.15 && price <= 0.325)
        {
            membership = ((1/.175)*(price-0.15));
        }
        else if(price > 0.325 && price <= 0.5)
        {
            membership = (-1.0/0.175)*(price-0.325)+1;
        }
        return membership;
    }
    private double determineMDMembership(double price)
    {
        double membership = 0.0;
        
        if(price > 0.325 && price <= 0.5)
        {
            membership = (1/0.175)*(price-0.325);
        }
        else if(price > 0.5 && price <= 0.675)
        {
            membership = (-1/0.175)*(price-0.5)+1;
        }
        return membership;
    }
    private double determineHIMembership(double price)
    {
        double membership = 0.0;
        
        if(price > 0.5 && price <= 0.675)
        {
            membership = (1/0.175)*(price-0.5);
        }
        else if(price > 0.675 && price <= 0.85)
        {
            membership = (-1/0.175)*(price-0.675)+1;
        }
        return membership;
    }
    private double determineVHMembership(double price)
    {
        double membership = 0.0;
        
        if(price > 0.675 && price <= 0.85)
        {
            membership = (1/0.175)*(price-0.675);
        }
        else if(price > 0.85)
        {
            membership = 1.0;
        }
        return membership;
    }

    private double[] evaluateRules(double[] priceMembership, double[] madMembership) 
    {
        double[] ruleResult = new double[15];
        ruleResult[0] = this.vlAndNResult(priceMembership, madMembership);
        ruleResult[1] = this.loAndNResult(priceMembership, madMembership);
        ruleResult[2] = this.mdAndNResult(priceMembership, madMembership);
        ruleResult[3] = this.hiAndNResult(priceMembership, madMembership);
        ruleResult[4] = this.vhAndNResult(priceMembership, madMembership);
        ruleResult[5] = this.vlAndZResult(priceMembership, madMembership);
        ruleResult[6] = this.loAndZResult(priceMembership, madMembership);
        ruleResult[7] = this.mdAndZResult(priceMembership, madMembership);
        ruleResult[8] = this.hiAndZResult(priceMembership, madMembership);
        ruleResult[9] = this.vhAndZResult(priceMembership, madMembership);
        ruleResult[10] = this.vlAndPResult(priceMembership, madMembership);
        ruleResult[11] = this.loAndPResult(priceMembership, madMembership);
        ruleResult[12] = this.mdAndPResult(priceMembership, madMembership);
        ruleResult[13] = this.hiAndPResult(priceMembership, madMembership);
        ruleResult[14] = this.vhAndPResult(priceMembership, madMembership);
        return ruleResult;
    }
    
    private double vlAndNResult(double[] priceMembership, double[] madMembership)
    {
        return Math.min(priceMembership[0], madMembership[0]);
    }
    
    private double loAndNResult(double[] priceMembership, double[] madMembership)
    {
        return Math.min(priceMembership[1], madMembership[0]);
    }

    private double mdAndNResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[2], madMembership[0]);
    }

    private double hiAndNResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[3], madMembership[0]);
    }

    private double vhAndNResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[4], madMembership[0]);
    }

    private double vlAndZResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[0], madMembership[1]);
    }

    private double loAndZResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[1], madMembership[1]);
    }

    private double mdAndZResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[2], madMembership[1]);
    }

    private double hiAndZResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[3], madMembership[1]);
    }

    private double vhAndZResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[4], madMembership[1]);
    }

    private double vlAndPResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[0], madMembership[2]);
    }

    private double loAndPResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[1], madMembership[2]);
    }

    private double mdAndPResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[2], madMembership[2]);
    }

    private double hiAndPResult(double[] priceMembership, double[] madMembership) 
    {
        return Math.min(priceMembership[3], madMembership[2]);
    }

    private double vhAndPResult(double[] priceMembership, double[] madMembership) {
        return Math.min(priceMembership[4], madMembership[2]);
    }

    private double getSugenoWeightedAverage(double[] ruleEval, double[] sugenoConstants) 
    {
        double[] ruleResults = new double[15];
        ruleResults[0] = ruleEval[0]*sugenoConstants[4];
        ruleResults[1] = ruleEval[1]*sugenoConstants[4];
        ruleResults[2] = ruleEval[2]*sugenoConstants[4];
        ruleResults[3] = ruleEval[3]*sugenoConstants[4];
        ruleResults[4] = ruleEval[4]*sugenoConstants[4];
        ruleResults[5] = ruleEval[5]*sugenoConstants[1];
        ruleResults[6] = ruleEval[6]*sugenoConstants[1];
        ruleResults[7] = ruleEval[7]*sugenoConstants[1];
        ruleResults[8] = ruleEval[8]*sugenoConstants[1];
        ruleResults[9] = ruleEval[9]*sugenoConstants[1];
        ruleResults[10] = ruleEval[10]*sugenoConstants[0];
        ruleResults[11] = ruleEval[11]*sugenoConstants[0];
        ruleResults[12] = ruleEval[12]*sugenoConstants[0];
        ruleResults[13] = ruleEval[13]*sugenoConstants[0];
        ruleResults[14] = ruleEval[14]*sugenoConstants[0];
        double numerator = 0.0;
        double denominator = 0.0;
        for(int i=0; i<15; i++)
        {
            numerator += ruleResults[i];
            denominator += ruleEval[i];
        }
        return numerator/denominator;   
    }
}
